home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 …ember: Reference Library / Apple Developer Reference Library (December 1999) (Disk 1).iso / pc / technical documentation / develop / develop issue 26 / develop issue 26 code / truffles - display mgr. / sprocket / sources / futures.cp < prev    next >
Encoding:
Text File  |  1995-08-31  |  25.2 KB  |  733 lines

  1. /*
  2.     File:        Futures.c
  3.  
  4.     Contains:    Futures package
  5.  
  6.     This code goes to pains to NOT use the exception handler, and therefore also
  7.     avoids using AppleEventUtilities.cp, so that it will not be dependant on the
  8.     need to switch out gExceptionStack on every thread context switch, and therefore
  9.     should be able to run in conjunction with custom scheduler and custom thread
  10.     swap routines without need for modification.
  11.     
  12.     The futures package will spawn new threads in two places:  first, at InitFutures
  13.     time, a new thread may be spawned to call IdleFutures Periodicly.  Second,
  14.     the predispatch handler, if installed, will fork a new thread every time an
  15.     AppleEvent is dispatched.
  16. */
  17.  
  18.  
  19. #include "Futures.h"
  20. #include "Semaphores.h"
  21.  
  22. #ifndef __APPLEEVENTS__
  23. #include <AppleEvents.h>
  24. #endif
  25.  
  26. //
  27. // Keywords for special handlers not documented
  28. // in Inside Macintosh
  29. //
  30. #define keyAEBlock                    'blck'
  31. #define keyAEUnblock                'unbk'
  32.  
  33. //
  34. // Message ID of the event sent by AEResetTimer
  35. //
  36. #define kAEWaitLonger                'wait'
  37.  
  38. //
  39. // Random, arbitrarily choosen keyword 
  40. // for a parameter that probably doesn't exist.
  41. //
  42. #define keyNonexistantParameter        'nonx'
  43.  
  44. //
  45. // Structure used just to pass parameters to the "RedispatchEvent" thread
  46. //
  47. struct AsyncPredispatchParameters
  48. {
  49.     AppleEvent                        fAppleEvent;
  50.     AppleEvent                        fReply;
  51.     AEEventHandlerUPP                fEventHandler;
  52.     long                            fHandlerRefCon;
  53. };
  54.  
  55. typedef struct AsyncPredispatchParameters AsyncPredispatchParameters;
  56.  
  57. #if USESROUTINEDESCRIPTORS
  58.  
  59. //
  60. // The block and unblock special handlers are not
  61. // documented, so they do not have ProcInfo descriptions
  62. // for their callback in the Universal headers.  Both
  63. // the block and the unblock routine take a single parameter:
  64. // the AppleEvent that is being blocked on / unblocked.
  65. //
  66. enum
  67. {
  68.     uppAEBlockUnblockProcInfo = kPascalStackBased
  69.          | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
  70.          | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(AEDesc*)))
  71. };
  72.  
  73. #endif
  74.  
  75. //
  76. // Private constants:
  77. //
  78. #define kCreateSemaphoreIfNotFound            true
  79. #define kDontCreateSemaphoreIfNotFound        false
  80.  
  81.  
  82. //
  83. // Private functions:
  84. //
  85. static OSErr NewFuturesThread(ThreadEntryProcPtr threadEntry, void *threadParam, long handlerRefCon, ThreadID *threadMade);
  86. static TSemaphore* GetFutureSemaphore(AppleEvent* reply, Boolean createIfNotFound);
  87. static pascal OSErr AEBlock(AppleEvent* reply);
  88. static pascal OSErr AEUnblock(AppleEvent* reply);
  89. static pascal OSErr WaitLongerEvent(AppleEvent* ae, AppleEvent* reply, long refCon);
  90. static pascal OSErr AsyncPreDispatchHandler(AppleEvent* ae, AppleEvent* reply, long refCon);
  91. static void RedispatchEvent(void* threadParam);
  92. static OSErr GetAEMHandlerUPPFromOneTable(AEEventClass theAEEventClass, AEEventID theAEEventID, AEEventHandlerUPP *handler, long *handlerRefcon, Boolean isSysHandler);
  93. static OSErr GetAppleEventHandlerUPP(AppleEvent* ae, AEEventHandlerUPP *handler, long *handlerRefCon);
  94. static long PrivateFuturesThread(long threadParam);
  95. static void FillInDefaultTimeoutValues(long& timeout);
  96.  
  97.  
  98. #if USESROUTINEDESCRIPTORS
  99.  
  100. static RoutineDescriptor gAEBlockRD                        = BUILD_ROUTINE_DESCRIPTOR(uppAEBlockUnblockProcInfo, AEBlock);
  101. static RoutineDescriptor gAEUnblockRD                    = BUILD_ROUTINE_DESCRIPTOR(uppAEBlockUnblockProcInfo, AEUnblock);
  102. static RoutineDescriptor gWaitLongerEventRD                = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, WaitLongerEvent);
  103. static RoutineDescriptor gAsyncPreDispatchRD            = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, AsyncPreDispatchHandler);
  104.  
  105. #endif // USESROUTINEDESCRIPTORS
  106.  
  107. //
  108. // Globals:
  109. //
  110. ThreadCreateProcPtr gThreadCreateProc                    = nil;
  111.  
  112. //----------------------------------------------------------------------------------------
  113. // InitFutures: 
  114. //
  115. // Install the block and unblock routines.
  116. //
  117. // If spawnHousekeepingThread is true, then a thread will be forked to idle the
  118. // Futures package.  If you pass false for this boolean, your code must call
  119. // IdleFutures Periodicly.
  120. //
  121. // If installAsyncPreDispatchHandler is true, then the futures package will
  122. // install a predispatch handler that automatically forks a new thread every
  123. // time AEProcessAppleEvents is called, so that all of your application's
  124. // event handlers may be processed asynchronously.
  125. //----------------------------------------------------------------------------------------
  126. OSErr InitFutures(ThreadCreateProcPtr threadCreateProc, long initFuturesFlags)
  127. {
  128.     ThreadID futuresHousekeepingThreadID = kNoThreadID;
  129.     OSErr err = noErr;
  130.  
  131.     TSemaphore::InitializeGlobals();
  132.     
  133.     gThreadCreateProc = threadCreateProc;
  134.     
  135. #if USESROUTINEDESCRIPTORS
  136.     if(err == noErr)
  137.         err = AEInstallSpecialHandler(keyAEBlock, &gAEBlockRD, false);
  138.     if(err == noErr)
  139.         err = AEInstallSpecialHandler(keyAEUnblock, &gAEUnblockRD, false);
  140.     if(err == noErr)
  141.         err = AEInstallEventHandler(kCoreEventClass, kAEWaitLonger, &gWaitLongerEventRD, 0, false);
  142. #else
  143.     if(err == noErr)
  144.         err = AEInstallSpecialHandler(keyAEBlock, (UniversalProcPtr) AEBlock, false);
  145.     if(err == noErr)
  146.         err = AEInstallSpecialHandler(keyAEUnblock, (UniversalProcPtr) AEUnblock, false);
  147.     if(err == noErr)
  148.         err = AEInstallEventHandler(kCoreEventClass, kAEWaitLonger, (AEEventHandlerProcPtr) &WaitLongerEvent, 0, false);
  149. #endif
  150.     
  151.     //
  152.     // Create the housekeeping thread.
  153.     // We could probably use a very small stack for this thread.
  154.     //
  155.     if((err == noErr) && (initFuturesFlags & kSpawnHousekeepingThread))
  156.         err = NewFuturesThread((ThreadEntryProcPtr)PrivateFuturesThread, 0, 0, &futuresHousekeepingThreadID);
  157.     
  158.     //
  159.     // If an async pre-dispatch handler was requested, then install it.
  160.     //
  161.     if((err == noErr) && (initFuturesFlags & kInstallAsyncPreDispatchHandler))
  162.     {
  163. #if USESROUTINEDESCRIPTORS
  164.         err = AEInstallSpecialHandler(keyPreDispatch, &gAsyncPreDispatchRD, false);
  165. #else
  166.         err = AEInstallSpecialHandler(keyPreDispatch, (UniversalProcPtr)AsyncPreDispatchHandler, false);
  167. #endif        
  168.     }
  169.     
  170.     return err;
  171. } // InitFutures 
  172.  
  173. //----------------------------------------------------------------------------------------
  174. // BlockUntilReal: 
  175. //
  176. // Block the current thread until the specified message becomes real.  This function
  177. // works by accessing a nonexistant parameter of the reply; the AppleEvent manager will
  178. // block any access to the reply, since (obviously) it doesn't know what the parameters
  179. // of the reply will be until the reply actually arrives.
  180. //----------------------------------------------------------------------------------------
  181. void BlockUntilReal(AppleEvent* reply)
  182. {
  183.     long            nonExistentParameterStorage;
  184.     long            actualSize;
  185.     DescType        typeCode;
  186.  
  187.     //
  188.     // Ask for the non existent parameter; this will cause the AppleEvent
  189.     // manager to block on the event
  190.     //
  191.     AEGetParamPtr(reply, keyNonexistantParameter, typeLongInteger, &typeCode, (Ptr) &nonExistentParameterStorage, sizeof(long), &actualSize);
  192. } // BlockUntilReal 
  193.  
  194. //----------------------------------------------------------------------------------------
  195. // ReplyArrived
  196. //
  197. // Return 'true' if the reply can be accessed without blocking, or false if it
  198. // is still a future
  199. //----------------------------------------------------------------------------------------
  200. Boolean ReplyArrived(AppleEvent* ae)
  201. {
  202.     UniversalProcPtr    oldBlockRoutine = nil;
  203.     long                nonExistentParameterStorage;
  204.     long                actualSize;
  205.     DescType            typeCode;
  206.     Boolean                arrived = false;
  207.  
  208.     OSErr err = AEGetSpecialHandler(keyAEBlock, &oldBlockRoutine, false);
  209.     if(err == noErr)
  210.         err = AERemoveSpecialHandler(keyAEBlock, oldBlockRoutine, false);
  211.  
  212.     //
  213.     // Ask for the non existent parameter; this will cause the AppleEvent
  214.     // manager to return errAEReplyNotArrived if the reply is still a future;
  215.     // some other error (such as paramter not found) will be returned if
  216.     // the reply has arrived
  217.     //
  218.     if(err == noErr)
  219.     {
  220.         arrived = (AEGetParamPtr(ae, keyNonexistantParameter, typeLongInteger, &typeCode, (Ptr) &nonExistentParameterStorage, sizeof(long), &actualSize) != errAEReplyNotArrived);
  221.         err = AEInstallSpecialHandler(keyAEBlock, oldBlockRoutine, false);
  222.     }
  223.  
  224.     return arrived;
  225. } // ReplyArrived
  226.  
  227. //----------------------------------------------------------------------------------------
  228. // SetReplyTimeoutValue
  229. //
  230. // Specify how long the reply should wait in between reset-timer notifications, and
  231. // how much total time the reply should wait before giving up on a server that keeps
  232. // reseting the timer but never delivers results.
  233. //----------------------------------------------------------------------------------------
  234. void SetReplyTimeoutValue(AppleEvent* reply, long timeoutValue /* = kNeverTimeoutSemaphore */, long maxWaitTime /* = kNeverTimeoutSemaphore */)
  235. {
  236.     FillInDefaultTimeoutValues(timeoutValue);
  237.     FillInDefaultTimeoutValues(maxWaitTime);
  238.     
  239.     TSemaphore* semaphore = GetFutureSemaphore(reply, kCreateSemaphoreIfNotFound);
  240.     if(semaphore)
  241.     {
  242.         // DebugStr("\pSet reply timout value");
  243.         semaphore->SetSemaphoreTimoutValue(timeoutValue);
  244.         semaphore->SetSemaphoreMaxWaitTime(maxWaitTime);        
  245.     }
  246.     else
  247.         DebugStr("\pCouldn't make semaphore to set timeout value");
  248. }
  249.  
  250. //----------------------------------------------------------------------------------------
  251. // IdleFutures
  252. //
  253. // Usually, this routine doesn't need to be called, as it is done automatically by
  254. // the private futures thread, installed by InitFutures.
  255. //----------------------------------------------------------------------------------------
  256. void IdleFutures()
  257. {
  258.     TSemaphore::Idle();
  259. }
  260.  
  261. //----------------------------------------------------------------------------------------
  262. // AskForFuture
  263. //----------------------------------------------------------------------------------------
  264. OSErr AskForFuture(AppleEvent* ae, AppleEvent* reply, long timeoutValue /* = kAEDefaultTimeout */, long maxWaitTime /* = 0x7FFFFFFF */, AESendMode sendMode, AESendPriority sendPriority /* = kAENormalPriority */)
  265. {
  266.     OSErr err = noErr;
  267.     
  268.     FillInDefaultTimeoutValues(timeoutValue);
  269.     
  270.     long resetFrequency = timeoutValue / 2;
  271.     
  272.     err = AEPutAttributePtr(ae, keyAEResetTimerFrequencyAttr, typeLongInteger, (Ptr)&resetFrequency, sizeof(long));
  273.     if(err == noErr)
  274.         err = AESend(ae, reply, sendMode | kAEWaitReply, sendPriority, 0, nil, nil);
  275.     if(err == errAETimeout)
  276.         err = noErr;
  277.     if(err == noErr)
  278.         SetReplyTimeoutValue(reply, timeoutValue, maxWaitTime);
  279.  
  280.     return err;
  281. }
  282.  
  283. //----------------------------------------------------------------------------------------
  284. // GetResetTimerFrequency
  285. //
  286. // Determines how frequently a server application is advised to call AEResetTimer
  287. //----------------------------------------------------------------------------------------
  288. long GetResetTimerFrequency(AppleEvent* ae)
  289. {
  290.     DescType typeCode;
  291.     long actualSize;
  292.     long resetTimerFrequency = 0;
  293.     
  294.     //
  295.     // First look at keyAEResetTimerFrequencyAttr; if that attribute
  296.     // does not exist, try half of keyAETimeoutAttr
  297.     //
  298.     if(AEGetAttributePtr(ae, keyAEResetTimerFrequencyAttr, typeLongInteger, &typeCode, (Ptr)&resetTimerFrequency, sizeof(long), &actualSize) != noErr)
  299.     {
  300.         if(AEGetAttributePtr(ae, keyTimeoutAttr, typeLongInteger, &typeCode, (Ptr)&resetTimerFrequency, sizeof(long), &actualSize) == noErr)
  301.             resetTimerFrequency /= 2;
  302.         else
  303.             resetTimerFrequency = 0;
  304.     }
  305.     
  306.     //
  307.     // If we couldn't find a frequency, use the default
  308.     //
  309.     if(resetTimerFrequency == 0)
  310.         resetTimerFrequency = kDefaultResetTimerFrequency;
  311.     
  312.     return resetTimerFrequency;
  313. }
  314.  
  315. //----------------------------------------------------------------------------------------
  316. // ResetTimerIfNecessary
  317. //
  318. // Calls AEResetTimer if enough time has elapsed.
  319. //----------------------------------------------------------------------------------------
  320. OSErr ResetTimerIfNecessary(AppleEvent* reply, unsigned long* lastReset, long resetFrequency)
  321. {
  322.     OSErr err = noErr;
  323.     unsigned long currentTime = TickCount();
  324.     
  325.     //
  326.     // Set up 'lastReset' the first time in
  327.     //
  328.     if(*lastReset == 0)
  329.         *lastReset = currentTime;
  330.     
  331.     //
  332.     // We won't be so extreme as to enforce a minimum reset
  333.     // frequency, but we don't allow zero!
  334.     //
  335.     if(resetFrequency == 0)
  336.         resetFrequency = kDefaultResetTimerFrequency;
  337.     
  338.     //
  339.     // TimeExpired is in Semaphores.c; it determines if 'currentTime' has
  340.     // gone beyond 'lastReset + resetFrequency', handling the unavoidable
  341.     // tickcount wraparound that happens every 771 days.
  342.     //    
  343.     if(TimeExpired(currentTime, *lastReset, *lastReset + resetFrequency))
  344.     {
  345.         // DebugStr("\pAbout to call AEResetTimer");
  346.         
  347.         *lastReset = currentTime;
  348.         err = AEResetTimer(reply);
  349.     }
  350.     
  351.     return err;
  352. }
  353.  
  354. //----------------------------------------------------------------------------------------
  355. // NewFuturesThread
  356. //
  357. // This routine calls either 'NewThread' or the application-defined thread creation
  358. // procedure.
  359. //----------------------------------------------------------------------------------------
  360. static OSErr NewFuturesThread(ThreadEntryProcPtr threadEntry, void *threadParam, long handlerRefCon, ThreadID *threadMade)
  361. {
  362.     OSErr err = noErr;
  363.     Size stackSize = 0;
  364.     ThreadOptions options = kCreateIfNeeded | kFPUNotNeeded;
  365.     
  366.     if(gThreadCreateProc != nil)
  367.         err = (*gThreadCreateProc)(threadEntry, threadParam, handlerRefCon, threadMade);
  368.     else
  369.         err = NewThread(kCooperativeThread, threadEntry, threadParam, stackSize, options, nil, threadMade);
  370.     
  371.     return err;
  372. }
  373.  
  374. //----------------------------------------------------------------------------------------
  375. // GetFutureSemaphore:
  376. //
  377. // This function returns the semaphore that this event is blocked on, or
  378. // nil if the event is not blocked
  379. //----------------------------------------------------------------------------------------
  380. static TSemaphore* GetFutureSemaphore(AppleEvent* reply, Boolean createIfNotFound)
  381. {
  382.     TSemaphore* semaphore = nil;
  383.     long semaphoreID = 0;
  384.  
  385.     //
  386.     // Use the reply's return ID as the semaphore ID.
  387.     //
  388.     // We need to set the semaphore ID when the current thread
  389.     // blocks on the future; once the reply arrives, we need to
  390.     // pull the semaphore ID out of the reply.  Therefore, we'd
  391.     // better use an attribute that exists before and after the
  392.     // reply arrives.
  393.     //
  394.     // Another important point:  we can access the return ID attribute
  395.     // without causing the block routine to be called--very important,
  396.     // as we need to get the semaphore out of the message from
  397.     // within the block routine!
  398.     //
  399.     DescType typeCode;
  400.     long actualSize;
  401.     if(AEGetAttributePtr(reply, keyReturnIDAttr, typeLongInteger, &typeCode, (Ptr)&semaphoreID, sizeof(long), &actualSize) == noErr)
  402.         semaphore = TSemaphore::FindSemaphore(semaphoreID, createIfNotFound, 0);
  403.     else
  404.         DebugStr("\pCould not get return attr from reply");
  405.     
  406.     return semaphore;
  407. } // GetFutureSemaphore 
  408.  
  409. //----------------------------------------------------------------------------------------
  410. // AEBlock: 
  411. //
  412. // This routine is installed as the special handler "block", which is called by the
  413. // AppleEvent manager whenever a routine that tries to extract information from a
  414. // future (an AppleEvent reply that has not yet arrived) is called.
  415. //----------------------------------------------------------------------------------------
  416. static pascal OSErr AEBlock(AppleEvent* reply)
  417. {
  418.     TSemaphore*            semaphore = nil;
  419.     OSErr                err = noErr;
  420.     
  421.     // DebugStr("\pAEBlock");
  422.     
  423.     //
  424.     // Look up the semaphore attached to this message.  This will
  425.     // only rarely exist; the only time that it would exist would
  426.     // be if multiple threads tried to pull information out of
  427.     // the same future.  In that case, the first thread would create
  428.     // the semaphore, and the second thread would then get and
  429.     // block on the same semaphore.
  430.     //
  431.     semaphore = GetFutureSemaphore(reply, kCreateSemaphoreIfNotFound);
  432.         
  433.     //
  434.     // If we have a semaphore, then block on it.
  435.     // 'errAETimeout' is one error that may
  436.     // come out of 'Grab' (if we go to sleep
  437.     // for a while, then give up).  Note, however,
  438.     // that the AppleEvent manager will always
  439.     // return errAEReplyNotArrived if AEBlock
  440.     // returns anything other than noErr.
  441.     //
  442.     if(semaphore != nil)
  443.     {
  444.         err = semaphore->Grab();
  445.         semaphore->Release();
  446.     }
  447.     //
  448.     // If we can't block, then return
  449.     // errAEReplyNotArrived right away
  450.     //
  451.     else
  452.         err = errAEReplyNotArrived;
  453.             
  454.     return err;
  455. } // AEBlock 
  456.  
  457. //----------------------------------------------------------------------------------------
  458. // AEUnblock: 
  459. //
  460. // This function is called by the AppleEvent manager when a future that was blocked
  461. // on becomes a real event (i.e., when the reply actually arrives).  When that happens,
  462. // we need to unblock all threads that are blocked on this future.
  463. //----------------------------------------------------------------------------------------
  464. static pascal OSErr AEUnblock(AppleEvent* reply)
  465. {
  466.     TSemaphore*            semaphore = nil;
  467.     OSErr                err = noErr;
  468.  
  469.     // DebugStr("\pAEUnblock");
  470.     
  471.     //
  472.     // The AppleEvent manager may call the unblock routine
  473.     // even if the block routine was never called, so there
  474.     // may or may not be a semaphore attached to the message
  475.     //
  476.     semaphore = GetFutureSemaphore(reply, kDontCreateSemaphoreIfNotFound);
  477.     if(semaphore != nil)
  478.     {
  479.         // DebugStr("\pAbout to release all threads");
  480.         semaphore->Dispose();
  481.     }
  482.     
  483.     return err;
  484. } // AEUnblock 
  485.  
  486. //----------------------------------------------------------------------------------------
  487. // WaitLongerEvent:
  488. //
  489. // Notify the reply that some activity was reported from the server. 
  490. //----------------------------------------------------------------------------------------
  491. static pascal OSErr WaitLongerEvent(AppleEvent* ae, AppleEvent* /*reply*/, long /*refCon*/)
  492. {
  493.     // DebugStr("\pWaitLongerEvent");
  494.     
  495.     //
  496.     // Note that the return ID of the wait longer event
  497.     // is the same as the return ID of the reply whose
  498.     // semaphore we want to look up; the reply to the
  499.     // wait longer event is not used for anything, because
  500.     // the AppleEvent manager sends the wait longer event
  501.     // "no reply".
  502.     //
  503.     TSemaphore* semaphore = GetFutureSemaphore(ae, kDontCreateSemaphoreIfNotFound);
  504.     if(semaphore)
  505.     {
  506.         // DebugStr("\pFound semaphore to reset timer on");
  507.         semaphore->ResetTimeoutTimer();
  508.     }
  509.     //else
  510.         // DebugStr("\pGot wait longer event but did not find semaphore");
  511.     
  512.     return noErr;
  513. } // WaitLongerEvent
  514.  
  515. //----------------------------------------------------------------------------------------
  516. // AsyncPreDispatchHandler: 
  517. //----------------------------------------------------------------------------------------
  518. static pascal OSErr AsyncPreDispatchHandler(AppleEvent* ae, AppleEvent* reply, long /*refCon*/)
  519. {
  520.     //
  521.     // By default, assume that we are not going to handle this event;
  522.     // If we return 'errAEEventNotHandled', the AppleEvent manager will
  523.     // continue dispatching this event.  We let the AEM handle events
  524.     // without any handler, and events that would be handled by a system
  525.     // event handler (which we know will never yield, and therefore
  526.     // do not need to run in a thread).
  527.     //
  528.     OSErr err = errAEEventNotHandled;
  529.     AsyncPredispatchParameters** dispatchParams = nil;
  530.     AEEventHandlerUPP handler = nil;
  531.     long handlerRefCon = 0;
  532.     
  533.     //
  534.     // Look up the event handler and refCon in the AppleEvent tables;
  535.     // if we can't find a handler, then we'll let the AppleEvent
  536.     // manager do the dispatching.
  537.     //
  538.     if(GetAppleEventHandlerUPP(ae, &handler, &handlerRefCon) == noErr)
  539.     {
  540.         dispatchParams = (AsyncPredispatchParameters**)NewHandle(sizeof(AsyncPredispatchParameters));
  541.         if(dispatchParams != nil)
  542.         {
  543.             //
  544.             // Save the AppleEvent message and reply.  We don't need to
  545.             // save the event handler's refCon, because the AppleEvent
  546.             // manager will look it up again when the event is redispatched.
  547.             //
  548.             (*dispatchParams)->fAppleEvent        = *ae;
  549.             (*dispatchParams)->fReply            = *reply;
  550.             (*dispatchParams)->fEventHandler    = handler;
  551.             (*dispatchParams)->fHandlerRefCon    = handlerRefCon;
  552.                 
  553.             //
  554.             // Make a new thread
  555.             //
  556.             ThreadID newThreadID;
  557.             if(NewFuturesThread((ThreadEntryProcPtr)RedispatchEvent, (void*)dispatchParams, handlerRefCon, &newThreadID) == noErr)
  558.             {
  559.                 dispatchParams = nil;
  560.  
  561.                 //
  562.                 // Always suspend the current event.  AEResumeTheCurrentEvent
  563.                 // will be called to redispatch the event after it has been
  564.                 // forked into a thread.
  565.                 //
  566.                 AESuspendTheCurrentEvent(ae);
  567.             
  568.                 //
  569.                 // Set 'err' to 'noErr', indicating that we have handled
  570.                 // the event.
  571.                 //
  572.                 err = noErr;
  573.             }
  574.         }
  575.     }
  576.  
  577.     //
  578.     // If the 'dispatchParams' were created, but we couldn't
  579.     // fork a new thread, then dispose of them here.  Otherwise,
  580.     // they will be disposed when the event is redispatched.
  581.     //
  582.     if(dispatchParams != nil)
  583.         DisposeHandle((Handle)dispatchParams);
  584.     
  585.     return err;
  586. } // AsyncPreDispatchHandler 
  587.  
  588.  
  589. //----------------------------------------------------------------------------------------
  590. // RedispatchEvent
  591. //----------------------------------------------------------------------------------------
  592. static void RedispatchEvent(void* threadParam)
  593. {
  594.     OSErr err = noErr;
  595.     
  596.     //
  597.     // Extract the AppleEvent, Reply, event handler UPP and
  598.     // message refCon from the thread parameter, which we
  599.     // know is a handle to our predispatch parameters. 
  600.     //
  601.     AsyncPredispatchParameters** dispatchParams = (AsyncPredispatchParameters**)threadParam;
  602.     AppleEvent ae = (*dispatchParams)->fAppleEvent;
  603.     AppleEvent reply = (*dispatchParams)->fReply;
  604.     AEEventHandlerUPP handler = (*dispatchParams)->fEventHandler;
  605.     long handlerRefCon = (*dispatchParams)->fHandlerRefCon;
  606.     DisposeHandle((Handle)dispatchParams);
  607.     
  608.     //
  609.     // Call the event handler directly
  610.     //
  611.     err = CallAEEventHandlerProc(handler, &ae, &reply, handlerRefCon);
  612.     
  613.     //
  614.     // If the event handler returned an error, we need to do
  615.     // exactly what the AppleEvent manager would do in the
  616.     // same situation:  jam the error into keyErrorNumber
  617.     // iff there isn't a keyErrorNumber in the reply already.
  618.     // This is necessary because there is no way to pass
  619.     // the error code into AEResumeTheCurrentEvent
  620.     //
  621.     if(err != noErr)
  622.     {
  623.         DescType actualType = typeNull;
  624.         long actualSize = 0;
  625.         long errorResult = 0;
  626.         
  627.         if(AEGetParamPtr(&reply, keyErrorNumber, typeLongInteger, &actualType, &errorResult, sizeof(long), &actualSize) != noErr)
  628.         {
  629.             errorResult = err;
  630.             AEPutParamPtr(&reply, keyErrorNumber, typeLongInteger, &errorResult, sizeof(long));
  631.         }
  632.     }
  633.     
  634.     //
  635.     // When our handler returns, call AEResumeTheCurrentEvent
  636.     // without dispatching, so that the AppleEvent manager
  637.     // will send the reply.
  638.     //
  639.     AEResumeTheCurrentEvent(&ae, &reply, (AEEventHandlerUPP)kAENoDispatch, 0);
  640. }
  641.  
  642. //----------------------------------------------------------------------------------------
  643. // GetAEMHandlerUPPFromOneTable
  644. //----------------------------------------------------------------------------------------
  645. static OSErr GetAEMHandlerUPPFromOneTable(AEEventClass theAEEventClass, AEEventID theAEEventID, AEEventHandlerUPP *handler, long *handlerRefCon, Boolean isSysHandler)
  646. {
  647.     OSErr err = AEGetEventHandler(theAEEventClass, theAEEventID, handler, handlerRefCon, isSysHandler);
  648.     if(err != noErr)
  649.         err = AEGetEventHandler(typeWildCard, theAEEventID, handler, handlerRefCon, isSysHandler);
  650.     if(err != noErr)
  651.         err = AEGetEventHandler(theAEEventClass, typeWildCard, handler, handlerRefCon, isSysHandler);
  652.     if(err != noErr)
  653.         err = AEGetEventHandler(typeWildCard, typeWildCard, handler, handlerRefCon, isSysHandler);
  654.         
  655.     return err;
  656. }
  657.  
  658. //----------------------------------------------------------------------------------------
  659. // GetAppleEventHandlerUPP
  660. //----------------------------------------------------------------------------------------
  661. static OSErr GetAppleEventHandlerUPP(AppleEvent* ae, AEEventHandlerUPP *handler, long *handlerRefCon)
  662. {
  663.     AEEventClass theAEEventClass;
  664.     AEEventID theAEEventID;
  665.     DescType actualType = typeNull;
  666.     long actualSize = 0;
  667.     
  668.     //
  669.     // Look up the event class and the event ID of the AppleEvent
  670.     // so that we can look it up in the event handler dispatch table
  671.     //
  672.     OSErr err = AEGetAttributePtr(ae, keyEventClassAttr, typeType, &actualType, &theAEEventClass, sizeof(AEEventClass), &actualSize);
  673.     if(err == noErr)
  674.         err = AEGetAttributePtr(ae, keyEventIDAttr, typeType, &actualType, &theAEEventID, sizeof(AEEventID), &actualSize);
  675.  
  676.     if(err == noErr)
  677.     {
  678.         err = GetAEMHandlerUPPFromOneTable(theAEEventClass, theAEEventID, handler, handlerRefCon, false);
  679.  
  680. #if 0
  681.     //
  682.     // We don't need to search the system event handler table, because we can
  683.     // let AEResumeTheCurrentEvent(kAEUseStandardDispatch) do that.  We know
  684.     // that system event handlers don't yield, so this is safe.
  685.     //
  686.         if(err != noErr)
  687.             err = GetAEMHandlerUPPFromOneTable(theAEEventClass, theAEEventID, handler, handlerRefCon, true);
  688. #endif
  689.     }
  690.     
  691.     return err;
  692. }
  693.  
  694.  
  695. //----------------------------------------------------------------------------------------
  696. // PrivateFuturesThread
  697. //
  698. // This thread does periodic tasks for the Futures package; currently, it only checks
  699. // to see if there are any futures that have timed out.  Note that TSemaphore::Idle
  700. // calls TickCount(), and performs no action most of the time.
  701. //
  702. // Unfortunately, there's no way for this thread to tell the thread manager that it
  703. // doesn't need to be swapped in for a number of ticks; it has to keep getting scheduled,
  704. // do nothing, get scheduled again, and so on for most of its life.
  705. //----------------------------------------------------------------------------------------
  706. static long PrivateFuturesThread(long /* threadParam */)
  707. {
  708.     for(;;)
  709.     {
  710.         IdleFutures();
  711.         YieldToAnyThread();
  712.     }
  713.     
  714.     return 0;
  715. }
  716.  
  717. //----------------------------------------------------------------------------------------
  718. // FillInDefaultTimeoutValues
  719. //
  720. // This function converts from negative default constants to the actual positive
  721. // number that the constant is translated into.
  722. //----------------------------------------------------------------------------------------
  723. static void FillInDefaultTimeoutValues(long& timeout)
  724. {
  725.     //
  726.     // ••• What should the default timeout be?
  727.     //
  728.     if(timeout == kAEDefaultTimeout)
  729.         timeout = 1200;
  730.     else if(timeout < 0)
  731.         timeout = 0x7FFFFFFF;
  732. }
  733.